[React] よーし! いっちょReactやってみっか! #7 ステート基本編
はじめに
CX事業本部の中安です。まいどです。
モバイルアプリエンジニアな自分がReact
を始めてみることにした
「よーし! いっちょReactやってみっか!」シリーズの続きです。
今回もよろしくお願いします。
前回は、コンポーネントにスタイルを当てる方法を書きました。
React Bootstrap
をnpm
でインストールして使ってみるということもやりました。
今回は状態管理「ステート」の基本的なことをやってみようと思います。
ステートとは
あらためて「ステートって何」って話ですけれども
コンポーネントには、コンポーネント自身の操作可能な"状態"を保管する場所が設けられます。
この状態管理に使う値、または仕組みのことをステート
と呼んでいます。
簡単にいうと「状態管理」とは、値が変わるとコンポーネントの見た目(状態)が変わるということですね。
基本キーワードを押さえる回では、
クラスコンポーネントではthis.state
、関数コンポーネントではHooks
を用いることにより、このステートを扱えるというところまで書きました。
実際にどのように扱うのか、そしてその結果どうなるのか、というところまでやってみたいと思います。
関数コンポーネントの準備
このシリーズではまだクラスコンポーネントについて詳しく書いていません。
クラスコンポーネントの作り方については別途ブログにまとめようと思うので、今回は関数コンポーネントでHooks
を使った方法に絞ろうと思います。
では、あらためてHello
という関数コンポーネントを作ります。
前回React Bootstrap
のコンポーネントを使うところまでやったので、今回も同じように使っていきます。
import React from 'react'; import ReactDOM from 'react-dom'; import 'bootstrap/dist/css/bootstrap.min.css'; import Card from 'react-bootstrap/Card'; const Hello = (props) => { return ( <Card> <Card.Body> <h1>Hello World!</h1> </Card.Body> </Card> ) } let dom = document.getElementById('root'); ReactDOM.render( <Hello />, dom);
まずは "Hello World!" が表示されるコンポーネントができたと思います。
フォームを設置
今回はここに入力フォームを設置します。
React Bootstrap
が用意しているForm
を使うことにします。
import React from 'react'; import ReactDOM from 'react-dom'; import 'bootstrap/dist/css/bootstrap.min.css'; import Card from 'react-bootstrap/Card'; import Form from 'react-bootstrap/Form'; const Hello = (props) => { return ( <Card> <Card.Body> <h1>Hello World!</h1> <Form.Control/> </Card.Body> </Card> ) }
ブラウザで見ると
こういう感じの見た目になりました。
作るもののイメージ
さて、現時点ではHello
コンポーネントはただ「Hello World!」を出すだけのコンポーネントです。
設置した入力フォームに入力した文字列に対して挨拶してくれるコンポーネントに作り変えたいと思います。
ここから「ステート」を使う話になります。
Hooks
関数コンポーネントでステートを扱うためにはHooks
を使います。
基本キーワード編でもHooks
は登場しましたが、
具体的な書き方としては今回が初登場です。
楽しそうなのでちょっとやってみましょうよ。
ステートフック
一言でHooks
といっても実は何個も種類があります。
今回はその内の関数コンポーネントをステート値を単純に使う「ステートフック」という仕組みを使うことになります。
インポートする
まず、ステートフックを使用するためにはインポートを変更しなければいけません。
React
をインポートしている行を下記のように書き換えます。
import React from 'react'; ↓ import React, { useState } from 'react';
{}
(ブレス)で囲まれたuseState
は、react
という読み込み元で定義されている「モジュール」に当たります。
各種「モジュール」を読み込みたい時はこのような書き方になります。
その他のインポート文がブレスで囲まれてないのは、読み込み元でデフォルト定義されているモジュールを使用しようとしているからです。
ステートフックを設定する
useState
が使えるようになったところでHello
コンポーネントにそれを取り入れてみます。
const Hello = (props) => { const [name, setName] = useState("World"); return ( <Card> <Card.Body> <h1>Hello World!</h1> <Form.Control/> </Card.Body> </Card> ) }
追記したのは2行目。return
の前です。
これがステートフックの基本的な定義の書き方になるそうです。
useState
を使うと、[]
(ブラケット)内にあるname
とsetName
に「値」と「値を更新する(状態を変える)関数」が代入されます。
useState
に与えている引数はname
の初期値として扱われます。
ただの変数定義なのですが、実質的にはまるでクラスのgetter
とsetter
を定義しているようですね。(getterと言い切ってしまうと関数なのか?と誤解されてしまいそうですが・・・)
const [《ゲッタ(値)》, 《セッタ(関数)》] = useState(《初期値》);
命名規則は上記のようにname
という状態を扱いたい場合は、
左側は名詞そのもののname
、
右側はset
を付けたキャメルケースでsetName
というようにするのがセオリーのようです。
ステートの値を使う
では、定義したステートを使ってみるのですが、まずは値の方のname
をコンポーネントに配置していきます。
const Hello = (props) => { const [name, setName] = useState("World"); return ( <Card> <Card.Body> <h1>Hello {name}!</h1> <Form.Control value={name} /> </Card.Body> </Card> ) }
このように<h1>
要素とフォームの値にname
を差し込みました。
JSX
上で変数をブレスで囲むと値が反映されるようになるわけですね。
※HTML
上だと属性値はダブルクォートで囲む必要がありますが、JSX
であると不要であるところを注意してください。
ブラウザで確認すると
このようになりました。
ステートの更新をする
次にsetName
を使ってみましょう。
const Hello = (props) => { const [name, setName] = useState("World"); return ( <Card> <Card.Body> <h1>Hello {name}!</h1> <Form.Control value={name} onChange={(e) => setName(e.target.value)} /> </Card.Body> </Card> ) }
このように書き換えました。
まずonChange
という属性ですが、これは「フォームの入力値が変わる度」というイベントになります。
なので、ここに渡すのはその時の処理をする関数を渡してやる必要があります。
JavaScript
を触ったことがある人にはお馴染みですが、イベントデータとして自動的に関数の引数に情報が渡されてきます。
これをe
という変数で受け取っています。
これにより、入力フォームに入力された値をe.target.value
という形で取得することができますね。
その取得できた値をsetName
に渡しています。
ここが今回のミソになります。
こうすることでname
の値が更新されることになります。
しかも、リアルタイムにです。
ブラウザで確認します。
画像であると分かりづらいですが、実際にブラウザで試してみてください。 フォームに入力された値がすぐさま「Hello」のあとに反映されるようになったと思います。
「反応」という意味を持つReact
の、このリアルタイム性がつまり状態操作ということですね。
ちなみに、このように属性が多くなってくると見づらくなってしまうので適宜改行したほうが良いですね。
<Card> <Card.Body> <h1>Hello {name}!</h1> <Form.Control value={name} onChange={(e) => setName(e.target.value)} /> </Card.Body> </Card>
値は直接変えれない
ステートの値は直接に変えてはいけないというルールがあります。
例えばonChange
の中身は下記のようには変更してはいけません。
(e) => setName(e.target.value) ↓ (e) => name = e.target.value
このようにname
に直接代入しようとどうなるでしょうか。
これは入力フォームの内容が変わった瞬間にエラーが表示されることになると思います。
const
で宣言しているのでname
は定数扱いになるわけで、その値に代入しようとするからですね。
ですのでname
の値を変えたい時はsetName
を確実に使うようにしましょう。
複数のステート
もちろん、1つのコンポーネントに対して1つしかステートが持てないというわけではありません。
useState
はいくつも並べることができます。
const Hello = (props) => { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [address, setAddress] = useState(""); const [age, setAge] = useState(0); : :
また、上記のように初期値には文字列や数値、ブール値などのプリミティブな値を渡すケースは多いかと思いますが、 オブジェクトも渡すこともできます。
const [person, setPerson] = useState({ name: "", email: "", address: "", age: 0 });
ただし、下記のように更新時に 他のキーも一緒に更新してやらないとデータの整合性が取れなくなってしまいます。
<Form.Control value={person.name} onChange={(e) => setPerson({ name: e.target.value, email: "", address: "", age: 0 })} />
「渡すことができる」けれども「渡すべきか」という使い所はちゃんと設計しないといけないですね。
フックのルール
公式ドキュメントには大事な「フックのルール」が記載さています。
トップレベル以外の箇所でフックを呼んではいけない
「トップレベル以外の箇所」というのは、ループや条件分岐やネストした関数の中のことです。
つまり
if (condition) { const [name, setName] = useState("World"); }
こういうことをしてはいけないということです。
フックは関数コンポーネントの内部のみで呼び出さなければならない
一部例外はありますが、フックを関数コンポーネント以外の箇所で使ってはいけません。
コード
ここまでのソースコードを一旦載せておきます。
import React, { useState } from 'react'; import ReactDOM from 'react-dom'; import 'bootstrap/dist/css/bootstrap.min.css'; import Card from 'react-bootstrap/Card'; import Form from 'react-bootstrap/Form'; const Hello = (props) => { const [name, setName] = useState("World"); return ( <Card> <Card.Body> <h1>Hello {name}!</h1> <Form.Control value={name} onChange={(e) => setName(e.target.value)} /> </Card.Body> </Card> ) } let dom = document.getElementById('root'); ReactDOM.render( <Hello />, dom);
最後に
というわけで、今回はステートについて基本的なところを書きました。
その方法として、関数コンポーネントでHooks
を使用して実際にリアルタイムに反映されるUIを作ってみましたというところです。
次回はクラス型コンポーネントをちょっとやってみましょうか。
では、またー。